<script setup>
import {
  ref,
  onMounted,
  getCurrentInstance,
  toRefs,
  inject,
  watch,
  computed
} from 'vue'
import { isMobile } from '../../../utils/host'
import { stt } from '../../../service/api'

const props = defineProps({
  lan: {
    type: String,
    default: ''
  },
  doc: {
    type: Object,
    default: () => {
      return {
        id: null,
        name: ''
      }
    }
  },
  isAnswering: {
    type: Boolean,
    default: false
  },
  isPending: {
    type: Boolean,
    default: false
  }
})

const { lan, doc, isAnswering, isPending } = toRefs(props)

const animateScale = inject('animateScale')

// sendInput发送信息
// stopAi: 触摸语音按钮，停止说话和动作
const Emit = defineEmits([
  'sendInput',
  'stopAi',
  'clearDoc',
  'pushAgent',
  'changeSetting',
  'changeModel'
]) // sendInput：发送信息 ； stopAi: 触摸语音按钮，停止说话和动作 changeSetting: 切换设置
const voiceTooShort = ref(false) // 说话时间太短
const LONGTAPTIME = 400
const MAX_SPEECH_LENGTH = 30
const { appContext } = getCurrentInstance()
const proxy = appContext.config.globalProperties
const config = proxy.$config

const inputVal = ref('') // input 框value
const inputType = ref('input') // input 默认打字输入   speech: 语音输入
const keyBoardStatus = ref('down') // 记录设备键盘默认状态 down：设备键盘没有弹起    up：设备键盘弹起
const canChangeInputType = ref(false) // 是否可以切换输入方式

const placeHolder = ref(`告诉${config.serviceName}您遇到的问题`)

// 模式切换
const showChangeMode = ref(false) // 是否显示问答模式切换面板
const networkStatus = ref(0) // 联网搜索状态
const datasetStatus = ref(0) // 本地知识库开启状态
const allModes = [
  {
    text: '智能选择',
    value: 'default',
    description: '智能识别需求，AI自主动态思考'
  },
  {
    text: '问答模式',
    value: 'knowledge',
    description: '优先从知识库检索，快速获取答案'
  },
  {
    text: '问数模式',
    value: 'dataInsight',
    description: '数据查询与分析'
  },
  {
    text: '办事模式',
    value: 'operationTask',
    description: '对话办事，直达服务'
  }
]
const settings = ref({
  showDataInsight: config.nlToSqlTypeList && config.nlToSqlTypeList.length,
  showOperationTask: config.chatSubappList && config.chatSubappList.length
})
// 动态计算 modes，根据 settings 过滤
const modes = computed(() => {
  return allModes.filter((mode) => {
    if (mode.value === 'dataInsight' && !settings.value.showDataInsight) {
      return false // 不显示 "问数模式"
    }
    if (mode.value === 'operationTask' && !settings.value.showOperationTask) {
      return false // 不显示 "办事模式"
    }
    return true // 其他模式默认显示
  })
})

const getModeIndex = (value) => {
  let ret = ''
  modes.value.forEach((item, index) => {
    if (item.value === value) {
      ret = index
    }
  })
  return ret
}
const localModeIndex = ref(0)
localModeIndex.value =
  getModeIndex(window.localStorage.getItem('modeText')) || 0
const modeName = ref(modes.value[localModeIndex.value].text)
const mode = ref(modes.value[localModeIndex.value].value)
const onConfirmMode = (e, index) => {
  if (e.value === mode.value) return
  localModeIndex.value = index
  mode.value = e.value
  modeName.value = e.text
  window.localStorage.setItem('modeText', e.value)
  proxy.$toast(`${e.text}`)
  showChangeMode.value = false
}
watch(
  mode,
  (val) => {
    if (val !== 'knowledge') {
      // 非问答模式重置联网搜索和本地知识库
      networkStatus.value = 0
      datasetStatus.value = 0
    }
    Emit('changeSetting', 'serviceModel', val)
  },
  {
    immediate: true
  }
)
watch(
  networkStatus,
  (val) => {
    Emit('changeSetting', 'networkFlag', val)
  },
  {
    immediate: true
  }
)
watch(
  datasetStatus,
  (val) => {
    Emit('changeSetting', 'datasetFlag', val)
  },
  {
    immediate: true
  }
)

// 大模型切换
const showChangeModel = ref(false)
const models = inject('models')
const handleShowChangeModel = () => {
  showChangeModel.value = !showChangeModel.value
}
const getModelIndex = (value) => {
  let ret = ''
  models.forEach((item, index) => {
    if (item.value === value) {
      ret = index
    }
  })
  return ret
}
const localModelIndex = ref(0)
localModelIndex.value =
  getModelIndex(window.localStorage.getItem('modelText')) || 0
const modelName = ref(models[localModelIndex.value].text)
const model = ref(models[localModelIndex.value].value)
const onConfirmModel = (e, index) => {
  if (e.value === model.value) return
  localModelIndex.value = index
  model.value = e.value
  modelName.value = e.text
  window.localStorage.setItem('modelText', e.value)
  proxy.$toast(`您选择了${e.text}模型`)
  showChangeModel.value = false
  // console.log(`output->localModelIndex`,localModelIndex)
  // console.log(`output->e`,e)
}
watch(
  model,
  (val) => {
    Emit('changeModel', val)
  },
  {
    immediate: true
  }
)

// 浏览器通用录制语音方法
const browserRecord = {
  rec: null,
  recBlob: null,
  /** 调用open打开录音请求好录音权限**/
  recOpen: () => {
    browserRecord.rec = null
    browserRecord.recBlob = null
    const newRec = Recorder({
      type: 'mp3',
      sampleRate: 16000,
      bitRate: 16, // mp3格式，指定采样率hz、比特率kbps，其他参数使用默认配置；注意：是数字的参数必须提供数字，不要用字符串；需要使用的type类型，需提前把格式支持文件加载进来，比如使用wav格式需要提前加载wav.js编码引擎
      onProcess: function (
        buffers,
        powerLevel,
        bufferDuration,
        bufferSampleRate,
        newBufferIdx,
        asyncEnd
      ) {}
    })
    newRec.open(
      function () {
        // 打开麦克风授权获得相关资源
        browserRecord.rec = newRec
        console.log('已打开录音，可以点击录制开始录音了')
        proxy.$toast.clear()
        inputType.value = 'speech'
      },
      function (msg, isUserNotAllow) {
        // 用户拒绝未授权或不支持
        proxy.$toast.clear()
        proxy.$toast({
          message: '当前应用版本不支持语音输入'
        })
        inputType.value = 'input'
      }
    )
  },
  /** 关闭录音，释放资源**/
  recClose: () => {
    inputType.value = 'input'
    if (browserRecord.rec) {
      browserRecord.rec.close()
      console.log('已关闭')
    } else {
      console.log('未打开录音')
    }
  },
  /** 开始录音**/
  recStart: () => {
    // 打开了录音后才能进行start、stop调用
    if (browserRecord.rec && Recorder.IsOpen()) {
      browserRecord.recBlob = null
      console.log('已开始录音...')
      browserRecord.rec.start()
      setRemainTime()
    } else {
      console.log('未打开录音')
    }
  },
  /** 结束录音，得到音频文件**/
  recStop: () => {
    if (!(browserRecord.rec && Recorder.IsOpen())) {
      console.log('未打开录音')
      return
    }
    browserRecord.rec.stop(
      function (blob, duration) {
        browserRecord.recBlob = blob
        if (!blob || blob.size === 0) {
          speachStatus.value = '1'
          inputType.value = 'input'
          proxy.$toast(
            '录音失败,请检查是否有其他应用正在录音，或着您可以手动输入问题'
          )
          return
        }
        const time = Date.now()
        const file = new File([blob], `${time}.mp3`, {
          type: 'audio/mp3',
          lastModified: time
        })
        const data = {
          file,
          language: lan.value
        }
        stt(data)
          .then((res) => {
            res = JSON.parse(res)
            if (res.status === 20000000) {
              if (res.result) {
                Emit('sendInput', res.result)
              } else {
                proxy.$toast('未识别到内容，请检查是否有其他应用占用录音')
              }
            } else {
              proxy.$toast('语音识别失败')
            }
            speachStatus.value = '1'
          })
          .catch(() => {
            speachStatus.value = '1'
            proxy.$toast('语音识别失败')
          })
      },
      function (msg) {
        console.log('录音失败')
      }
    )
  },
  /** 结束录音**/
  recCancel: () => {
    if (!(browserRecord.rec && Recorder.IsOpen())) {
      console.log('未打开录音')
      return
    }
    browserRecord.rec.stop(
      function (blob, duration) {
        browserRecord.recBlob = null
      },
      function (msg) {
        console.log('录音失败')
      }
    )
  }
}

// 封装录音流程
// 开始录音
const startRecordVoice = () => {
  console.log('调用浏览器通用开始录音方法')
  browserRecord.recStart()
}
// 停止录音
const stopRecordVoice = () => {
  console.log('调用浏览器通用停止录音方法')
  browserRecord.recStop()
}
// 取消录音
const cancelRecordVoice = () => {
  console.log('调用浏览器通用取消录音方法')
  browserRecord.recCancel()
  reset()
}

canChangeInputType.value = true

// 发送问题
const sendInput = () => {
  Emit('sendInput', inputVal.value)
  inputVal.value = ''
}
const stopInput = () => {
  Emit('stopAi')
}

const btnMsg = ref('按住说话')
// 触发超时自动发送
const overTimeAutoSend = ref(false)
// 监听手机键盘是否弹起落下
if (/Android/gi.test(navigator.userAgent)) {
  const innerHeight = window.innerHeight
  window.addEventListener('resize', () => {
    const newInnerHeight = window.innerHeight
    if (innerHeight > newInnerHeight) {
      // 键盘弹出事件处理
      setTimeout(() => {
        keyBoardStatus.value = 'up'
        scroll()
      }, 100)
    } else {
      // 键盘收起事件处理
      setTimeout(() => {
        keyBoardStatus.value = 'down'
        scroll()
      }, 100)
    }
  })
} else {
  window.addEventListener('focusin', (e) => {
    if (e.target.getAttribute('id') !== 'message-input') return
    // 键盘弹出事件处理
    setTimeout(() => {
      keyBoardStatus.value = 'up'
      scroll()
    }, 100)
  })
  window.addEventListener('focusout', () => {
    // 键盘收起事件处理
    setTimeout(() => {
      keyBoardStatus.value = 'down'
      // sendInput() // 模拟ios点击完成 失去焦点（ios点击虚拟键盘的完成按钮，没有办法监控得到）
      scroll()
    }, 100) // 100ms延时 是为了避免点击发送按钮时首先触发 focusout，然后发送按钮就直接消失的问题
  })
}

// 切换输入模式  录音权限
const changeType = (mode) => {
  console.log('普通浏览器环境切换输入模式')
  if (mode === 'speech') {
    browserRecord.recOpen()
    proxy.$toast.loading({
      message: '正在初始化录音权限,请稍后',
      forbidClick: true
    })
  } else {
    browserRecord.recClose()
  }
}
//  录制音频 长按事件、上滑取消逻辑
const speachStatus = ref('1') // 1未发送 2录音中 3发送中
const isSwipeUp = ref(false) // 是否触发了上划取消动作
const remainTime = ref(MAX_SPEECH_LENGTH) // 录音剩余时间
const remainTimer = ref(null)
const ABS = Math.abs
const touchObject = {
  params: {},
  touch: {},
  pretouch: {},
  movetouch: {},
  longTapTimeout: null,
  tapTimeout: null,
  handles: {
    longtap: [],
    swipeUp: [],
    end: []
  },
  _emit: function (type) {
    !this.handles[type] && (this.handles[type] = [])
    for (let i = 0, len = this.handles[type].length; i < len; i++) {
      typeof this.handles[type][i] === 'function' &&
        this.handles[type][i](this.params)
    }
    return true
  }
}
touchObject.handles.longtap.push(() => {
  btnMsg.value = '松开发送，时长不超过30秒'
  if (speachStatus.value === '3') {
    proxy.$toast({
      message: '请等待上条语音发送结束'
    })
  }
  if (speachStatus.value === '1') {
    speachStatus.value = '2'
    // 开始录音
    startRecordVoice()
  }
})
touchObject.handles.swipeUp.push(() => {
  isSwipeUp.value = true
})
touchObject.handles.end.push(() => {
  btnMsg.value = '按住说话'
  // 触发超时自动发送，不在继续处理
  if (overTimeAutoSend.value) {
    clearTimeout(remainTimer.value)
    overTimeAutoSend.value = false
    return
  }
  if (remainTime.value >= MAX_SPEECH_LENGTH - 1 && isSwipeUp.value === false) {
    voiceTooShort.value = true
    // 取消录音
    cancelRecordVoice()
    proxy.$toast({
      message: '说话时间太短'
    })
  } else {
    remainTime.value = MAX_SPEECH_LENGTH
    if (isSwipeUp.value) {
      // 取消录音
      cancelRecordVoice()
    } else {
      speachStatus.value = '3'
      // 停止录音
      stopRecordVoice()
    }
    isSwipeUp.value = false
    clearTimeout(remainTimer.value)
  }
})
const touchStart = (e) => {
  Emit('stopAi')

  touchObject.params.event = e // 记录触摸时的事件对象,params为回调时的传参
  touchObject.e = e.target // 触摸的具体元素
  const point = e.touches ? e.touches[0] : e // 获得触摸参数
  const now = Date.now() // 当前的时间
  // 记录手指位置等参数
  touchObject.touch.startX = point.pageX
  touchObject.touch.startY = point.pageY
  touchObject.touch.startTime = now
  // 由于会有多次触摸的情况，单击事件和双击针对单次触摸，故先清空定时器
  touchObject.longTapTimeout && clearTimeout(touchObject.longTapTimeout)
  touchObject.tapTimeout && clearTimeout(touchObject.tapTimeout)
  touchObject.doubleTap = false
  touchObject._emit('touch') // 执行原生的touchstart回调，_emit为执行的方法，后面定义
  if (e.touches.length > 1) {
    // 这里为处理多个手指触摸的情况
  } else {
    touchObject.longTapTimeout = setTimeout(function () {
      // 手指触摸后立即开启长按定时器，800ms后执行
      touchObject._emit('longtap') // 执行长按回调
      touchObject.doubleTap = false
      e.preventDefault()
    }, LONGTAPTIME)
    // 按照上面分析的思路计算当前是否处于双击状态，ABS为全局定义的变量 var ABS = Math.abs;
    touchObject.doubleTap =
      touchObject.pretouch.time &&
      now - touchObject.pretouch.time < 300 &&
      ABS(touchObject.touch.startX - touchObject.pretouch.startX) < 30 &&
      ABS(touchObject.touch.startY - touchObject.pretouch.startY) < 30 &&
      ABS(touchObject.touch.startTime - touchObject.pretouch.time) < 300
    touchObject.pretouch = {
      // 更新上一个触摸的信息为当前，供下一次触摸使用
      startX: touchObject.touch.startX,
      startY: touchObject.touch.startY,
      time: touchObject.touch.startTime
    }
  }
}
const touchMove = (e) => {
  const point = e.touches ? e.touches[0] : e
  touchObject._emit('move') // 原生的touchmove事件回调
  if (e.touches.length > 1) {
    // multi touch
    // 多个手指触摸的情况
  } else {
    const diffX = point.pageX - touchObject.touch.startX
    const diffY = point.pageY - touchObject.touch.startY // 与手指刚触摸时的相对坐标
    touchObject.params.diffY = diffY
    touchObject.params.diffX = diffX
    if (touchObject.movetouch.x) {
      // 记录移动过程中与上一次移动的相对坐标
      touchObject.params.deltaX = point.pageX - touchObject.movetouch.x
      touchObject.params.deltaY = point.pageY - touchObject.movetouch.y
    } else {
      touchObject.params.deltaX = touchObject.params.deltaY = 0
    }
    if (ABS(diffX) > 30 || ABS(diffY) > 30) {
      // 当手指划过的距离超过了30，所有单手指非滑动事件取消
      touchObject.longTapTimeout && clearTimeout(touchObject.longTapTimeout)
      touchObject.tapTimeout && clearTimeout(touchObject.tapTimeout)
      touchObject.doubleTap = false
    }
    touchObject._emit('slide') // 执行自定义的move回调
    // 更新移动中的手指参数
    touchObject.movetouch.x = point.pageX
    touchObject.movetouch.y = point.pageY
  }
}
const touchEnd = (e) => {
  touchObject.longTapTimeout && clearTimeout(touchObject.longTapTimeout) // 手指离开了，就要取消长按事件
  const timestamp = Date.now()
  const deltaX = ~~((touchObject.movetouch.x || 0) - touchObject.touch.startX)
  const deltaY = ~~((touchObject.movetouch.y || 0) - touchObject.touch.startY)
  if (
    touchObject.movetouch.x &&
    (ABS(deltaX) > 30 || (touchObject.movetouch.y !== null && ABS(deltaY) > 30))
  ) {
    // swipe手势
    if (ABS(deltaX) < ABS(deltaY)) {
      if (deltaY < 0) {
        // 上划
        touchObject._emit('swipeUp')
        touchObject.params.direction = 'up'
      } else {
        // 下划
        touchObject._emit('swipeDown')
        touchObject.params.direction = 'down'
      }
    } else {
      if (deltaX < 0) {
        // 左划
        touchObject._emit('swipeLeft')
        touchObject.params.direction = 'left'
      } else {
        // 右划
        touchObject._emit('swipeRight')
        touchObject.params.direction = 'right'
      }
    }
    touchObject._emit('swipe') // 划
  } else {
    if (
      !touchObject.doubleTap &&
      timestamp - touchObject.touch.startTime < 300
    ) {
      // 单次点击300ms内离开，触发点击事件
      touchObject.tapTimeout = setTimeout(function () {
        touchObject._emit('tap')
        touchObject._emit('finish') // 事件处理完的回调
      }, 300)
    } else if (touchObject.doubleTap) {
      // 300ms内再次点击且离开，则触发双击事件，不触发单击事件
      touchObject._emit('dbtap')
      touchObject.tapTimeout && clearTimeout(touchObject.tapTimeout)
      touchObject._emit('finish')
    } else {
      touchObject._emit('finish')
    }
  }
  touchObject._emit('end') // 原生的touchend事件

  touchObject.params = {}
  touchObject.touch = {}
  touchObject.pretouch = {}
  touchObject.movetouch = {}
  touchObject.longTapTimeout = null
  touchObject.tapTimeout = null
}
const touchCancel = (e) => {
  proxy.$toast({
    message:
      '语音输入被中断，请检查是否有其他应用正在录音，或着您可以手动输入问题'
  })
  reset()
}
// 倒计时
const setRemainTime = () => {
  if (remainTime.value === 0) {
    // 如果录制时间大于等于30s，触发自动发送
    overTimeAutoSend.value = true
    remainTime.value = MAX_SPEECH_LENGTH
    speachStatus.value = '3'
    btnMsg.value = '按住说话'
    return false
  } else {
    remainTime.value--
  }
  remainTimer.value = setTimeout(() => {
    setRemainTime()
  }, 1000)
}

const reset = () => {
  //   proxy.$toast({
  //     message: '语音输入被打断，请重新输入'
  //   })
  touchObject.params = {}
  touchObject.touch = {}
  touchObject.pretouch = {}
  touchObject.movetouch = {}
  touchObject.longTapTimeout = null
  touchObject.tapTimeout = null

  speachStatus.value = '1'
  clearTimeout(remainTimer.value)
  remainTime.value = MAX_SPEECH_LENGTH
  btnMsg.value = '按住说话'
}

const keyupEnter = (e) => {
  if (e.keyCode === 13) {
    !isAnswering.value && sendInput()
  }
}

const setHt = inject('setHt')
const agentType = inject('agentType')
const setAgentType = inject('setAgentType')
const hText = ref('')
const goAgent = (type) => {
  setAgentType(type)
  if (type === 1) {
    setHt()
    placeHolder.value = `告诉${config.serviceName}您遇到的问题`
    Emit('pushAgent', 'XYWD')
  } else if (type === 2) {
    hText.value = '制度问询'
    setHt({
      t1: '欢迎进入',
      t2: '制度问询',
      t3: '智能体'
    })
    placeHolder.value = '请输入您的提问'
    Emit('pushAgent', 'ZDWX')
  } else if (type === 3) {
    setHt({
      t1: '欢迎进入',
      t2: '智能问数',
      t3: '智能体'
    })
    placeHolder.value = `告诉${config.serviceName}您遇到的问题`
    Emit('pushAgent', 'ZNWS')
  } else if (type === 4) {
    setHt({
      t1: '欢迎进入',
      t2: '应用导航',
      t3: '智能体'
    })
    placeHolder.value = `告诉${config.serviceName}您遇到的问题`
    Emit('pushAgent', 'YYDH')
  }
}
const close = () => {
  setHt()
  setAgentType(1)
  hText.value = ''
  placeHolder.value = `告诉${config.serviceName}您遇到的问题`
  Emit('clearDoc')
}

watch(
  doc,
  (newvalue) => {
    if (doc.value.id) {
      setAgentType(2)
      hText.value = doc.value.name
      placeHolder.value = '请输入您的提问'
    }
  },
  {
    deep: true
  }
)

onMounted(() => {
  document.addEventListener('visibilitychange', function () {
    if (document.visibilityState === 'hidden') {
      inputType.value = 'input'
      browserRecord.recClose()
      reset()
    }
  })
})
</script>

<template>
  <div
    class="input-box"
    :class="{ 'input-box__zd': agentType === 2 }"
    @click.once="animateScale"
  >
    <div class="tag-wrapper">
      <div class="tag-content">
        <van-popover
          v-model:show="showChangeMode"
          trigger="manual"
          :show-arrow="false"
          placement="top-start"
        >
          <div class="popover-content-mode">
            <div class="exchange-text">
              <span>切换回答模式</span>
              <span class="up-img" @click="showChangeMode = false"></span>
            </div>
            <div
              class="model-item"
              v-for="(item, index) in modes"
              :key="index"
              :class="{ active: localModeIndex === index }"
              @click="onConfirmMode(item, index)"
            >
              <div class="model-item-info">
                <div class="model-item-text">{{ item.text }}</div>
                <div class="model-item-des">{{ item.description }}</div>
              </div>
              <div class="check"></div>
            </div>
          </div>
          <template #reference>
            <div
              class="exchange-content"
              :class="{ touch: showChangeMode }"
              @click="showChangeMode = !showChangeMode"
            >
              <div class="modelName">{{ modeName }}</div>
              <div class="change-model"></div>
            </div>
          </template>
        </van-popover>
        <div class="exchange-model" v-if="config.showBigModelList == 1">
          <van-popover
            v-model:show="showChangeModel"
            trigger="manual"
            :show-arrow="false"
            placement="top-start"
          >
            <div class="popover-content">
              <div class="exchange-text">
                <span>切换大模型</span>
                <span class="up-img" @click="showChangeModel = false"></span>
              </div>
              <div
                class="model-item"
                v-for="(item, index) in models"
                :key="index"
                :class="{ active: localModelIndex === index }"
                @click="onConfirmModel(item, index)"
              >
                <img :src="item.icon" />
                <div class="model-item-info">
                  <div class="model-item-text">{{ item.text }}</div>
                  <div class="model-item-des">{{ item.description }}</div>
                </div>
                <div class="check"></div>
              </div>
            </div>
            <template #reference>
              <div
                class="exchange-content"
                :class="{ touch: showChangeModel }"
                @click="handleShowChangeModel"
                v-if="mode == 'knowledge' || mode == 'default'"
              >
                <div class="modelName">{{ modelName }}</div>
                <div class="change-model"></div>
              </div>
            </template>
          </van-popover>
        </div>
        <div
          class="network"
          :class="{ 'touch-network': datasetStatus }"
          v-if="mode == 'knowledge'"
          @click="datasetStatus = datasetStatus == 0 ? 1 : 0"
        >
          <div class="dataset-icon"></div>
          <div class="network-text">本地知识库</div>
        </div>
        <div
          class="network"
          :class="{ 'touch-network': networkStatus }"
          v-if="config.netWorking && mode == 'knowledge'"
          @click="networkStatus = networkStatus == 0 ? 1 : 0"
        >
          <div class="network-icon"></div>
          <div class="network-text">联网搜索</div>
        </div>
      </div>
    </div>
    <div class="zdh" v-if="agentType === 2">
      <div class="wrap">
        <div class="icon" />
        <div class="text oneline">
          {{ hText }}
        </div>
        <div class="close" @click="close" />
      </div>
    </div>
    <div class="input-wrapper" v-if="inputType === 'input'">
      <input
        type="text"
        id="message-input"
        v-model="inputVal"
        :placeholder="placeHolder"
        @keyup.enter="keyupEnter"
      />
      <span
        class="stop"
        @click.stop="stopInput"
        v-if="isAnswering || isPending"
      />
      <template v-else>
        <span
          class="send"
          @click.stop="sendInput"
          v-if="keyBoardStatus === 'up' || !canChangeInputType || !isMobile()"
        />
        <span
          class="speech"
          @click.stop="changeType('speech')"
          v-if="keyBoardStatus === 'down' && canChangeInputType && isMobile()"
        />
      </template>
    </div>
    <div class="speech-wrapper" v-if="inputType === 'speech'">
      <span
        class="btn"
        @touchstart="touchStart"
        @touchend="touchEnd"
        @touchmove="touchMove"
        @touchcancel="touchCancel"
        :class="{ ing: speachStatus === '2', normal: speachStatus !== '2' }"
        >{{ btnMsg }}</span
      >
      <span class="keyboard" @click.stop="changeType('input')" />
    </div>
  </div>
  <div class="speech-box" v-if="speachStatus !== '1'">
    <div class="recording" v-if="speachStatus === '2'">
      <span>正在录音,剩余{{ remainTime }}秒</span>
      <div id="loading">
        <span />
        <span />
        <span />
        <span />
        <span />
        <span />
        <span />
        <span />
        <span />
        <span />
        <span />
        <span />
      </div>
      <span>松开发送,上滑取消</span>
    </div>
    <div class="sending" v-if="speachStatus === '3'">
      <van-loading color="white" size="0.8rem" />
      <span>发送中</span>
    </div>
  </div>
</template>

<style lang="less">
.input-box {
  position: fixed;
  bottom: 0.3rem;
  left: 0.32rem;
  right: 0.32rem;
  .tag-wrapper {
    width: calc(100% + 0.32rem);
    overflow-x: scroll;
    overflow-y: hidden;
    white-space: nowrap; /* 横向排列子元素 */
    -webkit-overflow-scrolling: touch; /* 确保触摸滚动流畅 */
    padding: 0;
    font-size: 0.24rem;
    margin-bottom: 0.16rem;
    padding-right: 0.32rem;
    padding-bottom: 1px;
    .tag-content {
      display: inline-flex;
      overflow-y: hidden;
    }
    &::-webkit-scrollbar {
      display: none;
    }
    .exchange-model {
      margin-left: 0.12rem;
    }
    .exchange-content {
      display: flex;
      align-items: center;
      background-color: #fff;
      border-radius: 0.28rem;
      border: 1px solid #fff;
      vertical-align: middle;
      height: 0.6rem;
      line-height: 0.6rem;
      .modelName {
        flex: 1;
        width: 1.2rem;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        text-align: center;
        margin-left: 0.2rem;
        vertical-align: middle;
        font-weight: 500;
        font-size: 0.24rem;
        color: #333333;
      }
      .change-model {
        width: 0.3rem;
        height: 0.3rem;
        margin-right: 0.15rem;
        background-image: url('../../../assets/img/exchangenew.png');
        background-size: 100%;
        background-repeat: no-repeat;
        background-position: center;
      }
    }
    .touch {
      background-color: #dff0ff;
      border: 1px solid #0281dd;
      .modelName {
        color: #0281dd;
      }
      .change-model {
        background-image: url('../../../assets/img/transblue.png');
      }
    }
    .network {
      display: flex;
      align-items: center;
      background-color: #fff;
      border-radius: 0.28rem;
      height: 0.6rem;
      line-height: 0.6rem;
      margin-left: 0.12rem;
      border: 1px solid #fff;
      .network-text {
        flex: 1;
        max-width: 1.84rem;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        text-align: center;
        margin-right: 0.2rem;
        vertical-align: middle;
        font-weight: 500;
        font-size: 0.24rem;
        color: #0a0a0a;
      }
      .network-icon {
        width: 0.36rem;
        height: 0.36rem;
        margin-left: 0.2rem;
        margin-right: 0.1rem;
        background-image: url('../../../assets/img/net.png');
        background-size: 100%;
        background-repeat: no-repeat;
        background-position: center;
      }
      .dataset-icon {
        width: 0.36rem;
        height: 0.36rem;
        margin-left: 0.2rem;
        margin-right: 0.1rem;
        background-image: url('../../../assets/img/dataset.png');
        background-size: 100%;
        background-repeat: no-repeat;
        background-position: center;
      }
    }
    .touch-network {
      background-color: #dff0ff;
      border: 1px solid #0281dd;
      .network-text {
        color: #0281dd;
      }
      .dataset-icon {
        background-image: url('../../../assets/img/datasetblue.png');
      }
      .network-icon {
        background-image: url('../../../assets/img/netblue.png');
      }
    }
  }
  .zdh {
    font-size: 0.28rem;
    padding: 0 0.32rem;
    .wrap {
      display: flex;
      align-items: center;
      height: 0.98rem;
      line-height: 0.98rem;
      flex: 1;
      display: flex;
      align-items: center;
      .icon {
        width: 0.3rem;
        height: 0.36rem;
        background-image: url('../../../assets/img/system.png');
        background-size: 100% 100%;
        margin-right: 0.15rem;
      }
      .text {
        flex: 1;
      }
      .close {
        width: 0.4rem;
        height: 0.4rem;
        background-image: url('../../../assets/img/close.png');
        background-size: 100% 100%;
      }
    }
  }
  .input-wrapper,
  .speech-wrapper {
    height: 0.96rem;
    display: flex;
    align-items: center;
    align-items: center;
    justify-content: space-between;
    font-size: 0.28rem;
    input {
      border: none;
      width: 6.06rem;
      height: 0.68rem;
      border-radius: 0.08rem;
      padding: 0.18rem 0.2rem;
    }
    .stop {
      width: 0.48rem;
      height: 0.48rem;
      background-image: url('../../../assets/img/stop.png');
      background-size: 100% 100%;
    }
    .send {
      width: 0.48rem;
      height: 0.48rem;
      background-image: url('../../../assets/img/send-1.png');
      background-size: 100% 100%;
    }
    .speech {
      width: 0.48rem;
      height: 0.48rem;
      background-image: url('../../../assets/img/speech.png');
      background-size: 100% 100%;
    }
  }
  .input-wrapper {
    background-color: white;
    border-radius: 0.8rem;
    padding: 0 0.32rem;
  }
  .speech-wrapper {
    color: #fff;
    position: relative;
    padding-right: 0.32rem;
    .btn {
      width: 5.8rem;
      height: 0.68rem;
      line-height: 0.68rem;
      text-align: center;
      border-radius: 0.35rem;
    }
    .normal {
      background: linear-gradient(236deg, #52cfff 0%, #28a2fe 100%);
    }
    .ing {
      background: rgba(76, 178, 255, 0.9);
      // background-color: red;
    }
    .keyboard {
      width: 0.48rem;
      height: 0.48rem;
      background-image: url('../../../assets/img/keyboard.png');
      background-size: 100% 100%;
    }
  }
}

.input-box__zd {
  background-color: white;
  border-radius: 0.48rem;
  .input-wrapper {
    background-color: transparent;
  }
  .speech-wrapper {
    padding-left: 0.32rem;
    .btn {
      width: 5.5rem;
    }
  }
}
.speech-box {
  width: 2.6rem;
  height: 2.6rem;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: #7a92ad;
  border-radius: 0.16rem;
  text-align: center;
  font-size: 0.2rem;
  color: white;
  padding: 0.28rem;
  z-index: 99;
  div {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    #loading {
      width: 100%;
      height: 0.6rem;
      display: flex;
      flex-direction: row;
      justify-content: space-between;
      align-items: center;
      span {
        animation: load 0.8s ease infinite;
        display: inline-block;
        width: 0.06rem;
        height: 0.6rem;
        background-color: white;
        &:nth-child(1) {
          animation-delay: 0.2s;
        }
        &:nth-child(2) {
          animation-delay: 0.25s;
        }
        &:nth-child(3) {
          animation-delay: 0.3s;
        }
        &:nth-child(4) {
          animation-delay: 0.35s;
        }
        &:nth-child(5) {
          animation-delay: 0.25s;
        }
        &:nth-child(6) {
          animation-delay: 0.2s;
        }
        &:nth-child(7) {
          animation-delay: 0.25s;
        }
        &:nth-child(8) {
          animation-delay: 0.3s;
        }
        &:nth-child(9) {
          animation-delay: 0.35s;
        }
        &:nth-child(10) {
          animation-delay: 0.1s;
        }
        &:nth-child(11) {
          animation-delay: 0.25s;
        }
        &:nth-child(12) {
          animation-delay: 0.2s;
        }
      }
    }
  }
  .sending {
    padding-top: 0.5rem;
    justify-content: space-around;
    align-items: center;
  }
}
@keyframes load {
  0%,
  100% {
    height: 0.6rem;
    background-color: #ffffff;
  }
  50% {
    height: 0.2rem;
    background-color: #f9f5f5;
  }
}
.van-popup {
  font-size: 0.24rem;
  .popover-content {
    width: 4.7rem;
    border-radius: 0.2rem 0 0.2rem 0.2rem;
    overflow-y: auto;
    padding-bottom: 0.3rem;
    max-height: 50vh;
    overflow-y: auto;
    position: relative;
    .exchange-text {
      position: -webkit-sticky; /* Safari */
      padding-top: 0.24rem;
      padding-bottom: 0.2rem;
      padding-left: 0.3rem;
      background-color: #fff;
      font-weight: 600;
      font-size: 0.26rem;
      color: #bcbcbc;
      // 固定在父元素顶部
      position: sticky;
      top: 0;
      z-index: 999;
      display: flex;
      align-items: center;
      justify-content: space-between;
      .up-img {
        width: 0.3rem;
        height: 0.3rem;
        background-image: url('../../../assets/img/uptag.png');
        background-size: 100% 100%;
        margin-right: 0.24rem;
      }
    }
    .model-item {
      margin: 0 0.15rem;
      display: flex;
      padding: 0.2rem;
      border-radius: 0.08rem;
      position: relative;
      padding-right: 0.7rem;
      &-info {
        flex: 1;
      }
      &-text {
        font-size: 0.26rem;
        color: #333;
        font-weight: 500;
      }
      &-des {
        padding-top: 5px;
        font-size: 0.24rem;
        color: #aaaaaa;
        // 超过两行省略
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 2;
        overflow: hidden;
      }
      .check {
        position: absolute;
        right: 0.2rem;
        top: 50%;
        transform: translateY(-50%);
        width: 0.32rem;
        height: 0.32rem;
      }
      img {
        width: 0.4rem;
        height: 0.4rem;
        margin-right: 0.2rem;
        // border-radius: 50%;
      }
    }
    .active {
      background-color: #f2f2f2;
      .model-item-text {
        color: #000;
      }
      .model-item-des {
        color: #555555;
      }
      .check {
        background-image: url('../../../assets/img/check1.png');
        background-size: 100% 100%;
      }
    }
  }
  .popover-content-mode {
    width: 4.6rem;
    border-radius: 0.2rem 0 0.2rem 0.2rem;
    overflow-y: auto;
    padding-bottom: 0.3rem;
    max-height: 50vh;
    overflow-y: auto;
    position: relative;
    .exchange-text {
      position: -webkit-sticky; /* Safari */
      padding-top: 0.24rem;
      padding-bottom: 0.2rem;
      padding-left: 0.3rem;
      font-weight: 500;
      font-size: 0.26rem;
      font-weight: 600;
      color: #bcbcbc;
      position: sticky;
      top: 0;
      z-index: 999;
      display: flex;
      align-items: center;
      justify-content: space-between;
      .up-img {
        width: 0.3rem;
        height: 0.3rem;
        background-image: url('../../../assets/img/uptag.png');
        background-size: 100% 100%;
        margin-right: 0.24rem;
      }
    }
    .model-item {
      margin: 0 0.15rem;
      display: flex;
      padding: 0.24rem;
      border-radius: 0.08rem;
      position: relative;
      padding-right: 0.7rem;
      &-info {
        flex: 1;
      }
      &-text {
        font-size: 0.26rem;
        color: #555555;
        font-weight: 500;
      }
      &-des {
        padding-top: 5px;
        font-size: 0.24rem;
        color: #aaaaaa;
        // 超过两行省略
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 2;
        overflow: hidden;
      }
      .check {
        position: absolute;
        right: 0.2rem;
        top: 50%;
        transform: translateY(-50%);
        width: 0.32rem;
        height: 0.32rem;
      }
      img {
        width: 0.4rem;
        height: 0.4rem;
        margin-right: 0.2rem;
        // border-radius: 50%;
      }
    }
    .active {
      background-color: #f2f2f2;
      .model-item-text {
        color: #555555;
      }
      .model-item-des {
        color: #555555;
      }
      .check {
        background-image: url('../../../assets/img/check1.png');
        background-size: 100% 100%;
      }
    }
  }
}
</style>
